package com.googlecode.gwtresume.client;


import static com.googlecode.gwtresume.client.api.ResumeConstants.CLASSIC;
import static com.googlecode.gwtresume.client.api.ResumeConstants.LESS_DETAIL;
import static com.googlecode.gwtresume.client.api.ResumeConstants.MODERN;
import static com.googlecode.gwtresume.client.api.ResumeConstants.UNSTYLED;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.InlineHyperlink;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.xml.client.XMLParser;
import com.googlecode.gwtresume.client.api.ResumeConstants;
import com.googlecode.gwtresume.client.api.XmlResume;
import com.googlecode.gwtresume.client.render.ClassicRenderer;
import com.googlecode.gwtresume.client.render.ModernRenderer;
import com.googlecode.gwtresume.client.render.UnstyledRenderer;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Resume implements EntryPoint, ValueChangeHandler, RequestCallback {
	/**
	 * The message displayed to the user when the server cannot be reached or
	 * returns an error.
	 */
	private static final String SERVER_ERROR = "An error occurred while "
			+ "attempting to contact the server. Please check your network "
			+ "connection and try again.";


	private static final ResumeConstants constants = GWT.create(ResumeConstants.class);
	
	boolean init = true;
	XmlResume resume;
	final PopupWidget dialogBox = new PopupWidget("Resume Renderer");
	
	final List<RendererOption> options = new ArrayList<RendererOption>();
	
	final RendererOptionImpl detailOption = new RendererOptionImpl(true, constants.detail(), LESS_DETAIL, constants.detailMap());
	final RendererOptionImpl themeOption = new RendererOptionImpl(true, constants.theme(), MODERN, constants.themeMap());
	
	/*
	final RendererOptionImpl localeOption = new RendererOptionImpl(false, constants.locale(), LocaleInfo.getCurrentLocale().getLocaleName(), getLocaleOptions()) {
		protected Widget render(String label, String value,String token) {
			String url = GWT.getHostPageBaseURL() + "Resume.html?locale=" + value + "#" + token;
			return new Anchor(label, url);			
		}
		public String getCurrentValue() {
			return LocaleInfo.getCurrentLocale().getLocaleName();
		}
		
	};
	*/
	
	class RendererOptionImpl implements RendererOption {
		
		final boolean isHistoryToken;
		final String label;
		final String defaultValue;
		final Map<String,String> optionMap;
		private String currentValue;
		
		public RendererOptionImpl(boolean isHistoryToken, String label, String defaultValue, Map<String,String> optionMap) {
			super();
			this.isHistoryToken = isHistoryToken;
			this.label = label;
			this.defaultValue = defaultValue;
			this.optionMap = optionMap;
			setCurrentValue(defaultValue);
			options.add(this);
		}

		public boolean isHistoryToken() {
			return isHistoryToken;
		}
		
		public String getLabel() {
			return label;
		}
		
		public String getCurrentValue() {
			return currentValue;
		}

		public void setCurrentValue(String currentValue) {
			this.currentValue = currentValue;
		}

		public Widget[] getOptions(boolean excludeCurrent) {
			Widget[] altLinks = new Widget[excludeCurrent ? optionMap.size() - 1 : optionMap.size()];
			List<String> currentValues = getCurrentOptionValues();
			int i = 0;
			int idx = options.indexOf(this);
			for (String value : optionMap.keySet()) {
				
				boolean isCurrent = value.equals(getCurrentValue());
				if (isHistoryToken()) currentValues.set(idx, value);
				if (!isCurrent || !excludeCurrent) {
					if (i < altLinks.length)
						altLinks[i++] = render(optionMap.get(value), value, toToken(currentValues));
					else
						System.err.println("Error resolving value: " + value);
				}
				
			}
			return altLinks;
		}

		protected Widget render(String label, String value, String token) {
			return new InlineHyperlink(label, token);
		}
		
	}
	
	public List<String> getCurrentOptionValues() {
		List<String> values = new ArrayList<String>();
		for (RendererOption opt : options) {
			if (opt.isHistoryToken()) values.add(opt.getCurrentValue());
		}
		return values;
	}
	
	public static String toToken(List<String> values) {
		StringBuffer sb = new StringBuffer();
		for (String value : values) {
			if (sb.length() > 0) sb.append("-");
			sb.append(value);
		}
		return sb.toString();
	}
	
	public void setOptionValues(String token) throws Exception {
		String[] opts = token.split("-");
		if (opts.length == 2) { // TODO: rework options
			int i = 0;
			for (RendererOption opt : options) {
				if (opt.isHistoryToken()) {
					if (i >= opts.length) {
						throw new Exception("Expected " + (i + 1) + "tokens; found "+ opts.length + ": " + token);
					} else {
						options.get(i).setCurrentValue(opts[i]);
						i++;
					}
				}
			}
			render();
		}
		
	}
	
	public Map<String,String> getLocaleOptions() {
		Map<String,String> opts = new HashMap<String, String>();
		for (String locale : LocaleInfo.getAvailableLocaleNames()) {
			if (locale.equals("default")) continue;
			opts.put(locale, LocaleInfo.getLocaleNativeDisplayName(locale));
		}
		return opts;
	}
	
	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		GWT.setUncaughtExceptionHandler(dialogBox);
		
	    // If the application starts with no history token, redirect to default state
	    String initToken = History.getToken();
	    if (initToken.length() == 0) History.newItem(toToken(getCurrentOptionValues()));

		
		RootPanel.get().setTitle(constants.resume());
		
	    //RootPanel.get("controller").add(controlPanel);
	    
	    
	    
	    // Add history listener
	    History.addValueChangeHandler(this);

	    loadXmlResume();
		
		
		
		
	}
	
	void loadXmlResume() {
		try {
		  String url = GWT.getModuleBaseURL() + "Resume.xml";
		  //System.err.println("GET: " + url);
		  RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
		  rb.setCallback(this);
		  rb.send();
		} catch (Exception e) {
			dialogBox.handleError(e);
		}
	}
	
	
	public void onError(Request request, Throwable exception) {
		dialogBox.handleError(exception);
		
	}

	public void onResponseReceived(Request request, Response response) {
        com.google.gwt.xml.client.Document dom = XMLParser.parse(response.getText());
        try {
        this.resume = new XmlResume(constants, dom);
        if (init) {
        	init = false;
        	History.fireCurrentHistoryState();
        }
        } catch (Exception e) {
        	dialogBox.handleError(e);
        }
	}

	public void onValueChange(ValueChangeEvent event) {
		  //System.err.println("onValueChange: " + event.getValue());
		  try {
			  setOptionValues((String) event.getValue());
			  
			  //render(); // to avoid reloading the resume


		  } catch (Exception e) {
			  dialogBox.handleError(e);
		  }
	}
	
	private void render() {
		try {
			RootPanel container = RootPanel.get("container");
			if (container.getWidgetCount() > 0) container.remove(0);
			
			String detail = detailOption.getCurrentValue();
			String theme = themeOption.getCurrentValue();
				
			RootPanel.get().setStyleName("theme-" + theme);
			
			Renderer renderer = createRenderer(theme);
			

			container.add(renderer.render(resume, detail.equals(constants.MORE_DETAIL), options));
		} catch (Throwable x) {
			dialogBox.handleError(x);
		}		
	}
	
	private Renderer createRenderer(String theme) throws Exception {
		if (theme.equals(CLASSIC)) return new ClassicRenderer();
		else if (theme.equals(UNSTYLED)) return new UnstyledRenderer();
		else if (theme.equals(MODERN)) return new ModernRenderer();
		else throw new Exception("Unknown theme: " + theme);

	}
	
	class PopupWidget extends DialogBox implements GWT.UncaughtExceptionHandler {
		
		final Label message = new Label();
		final Button closeButton = new Button("Close");
		
		PopupWidget(String text) {
			setText(text);
			setAnimationEnabled(true);
			
			// We can set the id of a widget by accessing its Element
			closeButton.getElement().setId("closeButton");
			VerticalPanel dialogVPanel = new VerticalPanel();
			dialogVPanel.addStyleName("dialogVPanel");
			dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
			dialogVPanel.add(message);
			dialogVPanel.add(closeButton);
			setWidget(dialogVPanel);
			
			// Add a handler to close the DialogBox
			closeButton.addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					hide();
				}
			});			
			
		}
		
		public void onUncaughtException(Throwable e) {
			handleError(e);
		}

		public void handleError(Throwable x) {
			x.printStackTrace();
			message.setText(x.toString());
			closeButton.setFocus(true);
			dialogBox.center();
			show();
		}
		
	}
	
	

}
